package com.base.storage.provider;

import cn.hutool.core.util.ObjectUtil;
import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.model.*;
import com.base.core.codec.Base64Utils;
import com.base.core.lang.StringUtils;
import com.base.core.utils.R;
import com.base.storage.config.CloudStorageConfig;
import com.base.storage.enums.BusinessCodeEnums;
import com.base.storage.service.CloudStorageService;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 阿里云存储
 */
public class AliyunCloudStorageService extends CloudStorageService {


    public AliyunCloudStorageService(CloudStorageConfig cloudStorageConfig) {
        super(cloudStorageConfig);
    }

    /**
     * 初始化 oss 客户端
     *
     * @return
     */
    private OSSClient init() {
       return new OSSClient(config.getEndPoint(), new DefaultCredentialProvider(config.getDstAccessKey(),
               config.getDstSecretKey()), new ClientConfiguration());
    }

    /**
     * 文件上传
     *
     * @param file 文件
     * @return 返回http地址
     */
    @Override
    public String upload(MultipartFile file) throws Exception {
        if (ObjectUtil.isEmpty(file)) {
            R.throwFail(BusinessCodeEnums.FILE_NOT_EXISTS, BusinessCodeEnums.FILE_NOT_EXISTS.getMessage());
        }

        //检查文件合法性
        if(!this.checkSize(file)){
            R.throwFail(BusinessCodeEnums.FILE_SIZE_LIMIT, BusinessCodeEnums.FILE_SIZE_LIMIT.getMessage());
        }

        if(!this.checkType(file)){
            R.throwFail(BusinessCodeEnums.FILE_TYPE_ILLEGAL, BusinessCodeEnums.FILE_TYPE_ILLEGAL.getMessage());
        }

        //文件名称
        String oldName = file.getOriginalFilename();
        //文件类型
        String fileType = file.getContentType();
        //文件后缀
        String postfix = oldName.substring(oldName.lastIndexOf(".") + 1);

        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
            R.throwFail(e.getMessage());
        }

        return upload(inputStream, fileType, getPath(config.getDstPrefix()) + "." + postfix);
    }

    /**
     * 上传文件
     * 主要兼容客户端经过base64位编码的文件
     *
     * @param base64
     * @return 返回http地址
     */
    @Override
    public String upload(String base64) {
        if (StringUtils.isEmpty(base64)) {
            R.throwFail(BusinessCodeEnums.FILE_NOT_EXISTS, BusinessCodeEnums.FILE_NOT_EXISTS.getMessage());
        }
        // 文件格式: "data:image/png;base64," + "图片的base64字符串"
        MultipartFile files = Base64Utils.base64ToMultipart(base64);

        //检查文件合法性
        if(!this.checkSize(files)){
            R.throwFail(BusinessCodeEnums.FILE_SIZE_LIMIT, BusinessCodeEnums.FILE_SIZE_LIMIT.getMessage());
        }

        if(!this.checkType(files)){
            R.throwFail(BusinessCodeEnums.FILE_TYPE_ILLEGAL, BusinessCodeEnums.FILE_TYPE_ILLEGAL.getMessage());
        }

        //老文件名
        String oldName = files.getOriginalFilename();
        //文件类型
        String fileType = files.getContentType();
        //文件后缀
        String postfix = oldName.substring(oldName.lastIndexOf(".") + 1);
        //新文件名
        String fileName = getPath(config.getDstPrefix()) + "." + postfix;

        InputStream inputStream = null;
        try {
            inputStream = files.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
            R.throwFail(e.getMessage());
        }

        return upload(inputStream, fileType, fileName);
    }

    /**
     * 上传文件
     * 主要兼容客户端经过base64位编码的文件
     *
     * @param base64
     * @param filename
     * @return 返回http地址
     */
    @Override
    public String upload(String base64, String filename) {
        if (StringUtils.isEmpty(base64)) {
            R.throwFail(BusinessCodeEnums.FILE_NOT_EXISTS, BusinessCodeEnums.FILE_NOT_EXISTS.getMessage());
        }
        // 文件格式: "data:image/png;base64," + "图片的base64字符串"
        MultipartFile files = Base64Utils.base64ToMultipart(base64);

        //检查文件合法性
        if(!this.checkSize(files)){
            R.throwFail(BusinessCodeEnums.FILE_SIZE_LIMIT, BusinessCodeEnums.FILE_SIZE_LIMIT.getMessage());
        }

        if(!this.checkType(files)){
            R.throwFail(BusinessCodeEnums.FILE_TYPE_ILLEGAL, BusinessCodeEnums.FILE_TYPE_ILLEGAL.getMessage());
        }

        //老文件名
        String oldName = files.getOriginalFilename();
        //文件类型
        String fileType = files.getContentType();
        //文件后缀
        String postfix = oldName.substring(oldName.lastIndexOf(".") + 1);
        //新文件名
        String fileName = getPath(config.getDstPrefix(), filename) + "." + postfix;

        InputStream inputStream = null;
        try {
            inputStream = files.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
            R.throwFail(e.getMessage());
        }

        return upload(inputStream, fileType, fileName);
    }


    /**
     * 文件上传
     *
     * @param inputStream 字节流
     * @param path        文件路径，包含文件名
     * @return 返回http地址
     */
    @Override
    protected String upload(InputStream inputStream, String fileType, String path) {
        //初始化客户端
        OSSClient client = null;
        try {
            client = init();
            // 创建上传Object的Metadata
            ObjectMetadata meta = new ObjectMetadata();
            // 设置上传内容类型
            meta.setContentType(fileType);
            //被下载时网页的缓存行为
            meta.setCacheControl("no-cache");
            //创建上传请求
            PutObjectRequest request = new PutObjectRequest(config.getDstBucketName(), path, inputStream, meta);
            //上传文件
            client.putObject(request);
            return path;
        } catch (OSSException | ClientException e) {
            R.throwFail(e.getMessage());
        } finally {
            if (client != null) {
                client.shutdown();
            }
        }
        return null;
    }

    /**
     * 通过文件名下载文件
     *
     * @param fileName      要下载的文件名
     *                      例如：4DB049D0604047989183CB68D76E969D.jpg
     * @param localFileName 本地要创建的文件名
     */
    @Override
    public void downloadFile(String fileName, String localFileName) {
        OSSClient client = null;
        try {
            client = init();
            // 下载OSS文件到本地文件。如果指定的本地文件存在会覆盖，不存在则新建。
            client.getObject(new GetObjectRequest(config.getDstBucketName(), fileName),
                    new File(localFileName));
        } finally {
            if (client != null) {
                client.shutdown();
            }
        }
    }

    /**
     * 列举 test 文件下所有的文件
     */
    public List<String> listFile() {
        List<String> list = new ArrayList<>();
        OSSClient client = null;
        try {
            client = init();
            // 构造ListObjectsRequest请求。
            ListObjectsRequest listObjectsRequest = new ListObjectsRequest(config.getDstBucketName());

            // 设置prefix参数来获取目录下的所有文件。
            listObjectsRequest.setPrefix(config.getDstPrefix());
            // 列出文件。
            ObjectListing listing = client.listObjects(listObjectsRequest);
            // 遍历所有文件。
            for (OSSObjectSummary objectSummary : listing.getObjectSummaries()) {
                //把key全部转化成可以访问的url
                String url = getOssUrl(client, objectSummary.getKey());
                list.add(url);
            }
        } finally {
            if (client != null) {
                client.shutdown();
            }
        }
        return list;
    }

    /**
     * @param fileUrl 需要删除的文件url
     * @return boolean 是否删除成功
     * @MethodName: deleteFile
     * @Description: 单文件删除
     * 例如：http://*.oss-cn-beijing.aliyuncs.com/4DB049D0604047989183CB68D76E969D.jpg
     */
    @Override
    public boolean deleteFile(String fileUrl) {
        OSSClient client = null;
        try {
            client = init();
            //根据url获取fileName
            String fileName = getFileName(fileUrl);
            if (config.getDstBucketName() == null || fileName == null) return false;
            GenericRequest request = new DeleteObjectsRequest(config.getDstBucketName()).withKey(fileName);
            client.deleteObject(request);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            if (client != null) {
                client.shutdown();
            }
        }
        return true;
    }

    /**
     * @param fileUrl 文件url
     * @return String fileName
     * @MethodName: getFileName
     * @Description: 根据url获取fileName
     */
    private String getFileName(String fileUrl) {
        String str = "aliyuncs.com/";
        int beginIndex = fileUrl.indexOf(str);
        if (beginIndex == -1)
            return null;
        return fileUrl.substring(beginIndex + str.length());
    }

    /**
     * 根据文件名生成文件的访问地址
     *
     * @param ossClient
     * @param key
     * @return
     */
    private String getOssUrl(OSSClient ossClient, String key) {
        Date expiration = new Date(new Date().getTime() + CloudStorageConfig.FILE_EXPIRATION_TIME);// 生成URL
        GeneratePresignedUrlRequest generatePresignedUrlRequest;
        generatePresignedUrlRequest = new GeneratePresignedUrlRequest(config.getDstBucketName(), key);
        generatePresignedUrlRequest.setExpiration(expiration);
        URL url = ossClient.generatePresignedUrl(generatePresignedUrlRequest);
        return url.toString();
    }

}
